----------------------------------------------------------------------------------
-- Company:        EECS 452
-- Engineer:       Kurt Metzger
-- 
-- Create Date:    21:06:42 04/29/2008 
-- Design Name: 
-- Module Name:    I2C_camera - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity I2C_camera is
    Port ( reg_addr : in  STD_LOGIC_VECTOR (7 downto 0);    -- register address
           send_data : in  STD_LOGIC_VECTOR (7 downto 0);   -- data value to send
           rcvd_data : out  STD_LOGIC_VECTOR (7 downto 0);  -- value read
           mode : in  STD_LOGIC_VECTOR (1 downto 0); -- 0 write value, 1 write address, 2 read value
           req_in : in  STD_LOGIC;                          -- request transfer
           ack_out : out  STD_LOGIC;                        -- ack response
           data : inout  STD_LOGIC := 'Z';                  -- serial data to/from camera
           i2c_clk : out  STD_LOGIC;                        -- serial clock to camera
           clk : in  STD_LOGIC);                            -- system clock
end I2C_camera;

architecture Behavioral of I2C_camera is

   signal counter : std_logic_vector(9 downto 0);          -- clock divide counter
   signal clock_enable : std_logic := '0';                 -- slow clock tic
   signal sr_out : std_logic_vector (7 downto 0);   		  -- output shift register
	signal sr_in : std_logic_vector (8 downto 0);           -- input shift register
   signal bit_counter : std_logic_vector (3 downto 0);     -- counts shifted bits
   signal phase_counter : std_logic_vector (1 downto 0);   -- counts command phases
	signal sw_start, sw_started, sw_done : std_logic :='0';
	signal cam_clk : std_logic := '1';
	
	type t_state is (s_idle, s_start, s_wait, s_done);
	signal state : t_state := s_idle;
	type t_state2 is (s2_idle, s2_start, s2_start2, s2_start3,
	    s2_phase, s2_phase2, s2_phase3, s2_phase4,
		 s2_end, s2_end2, s2_end3);
	signal state2 : t_state2:=s2_idle;

begin
 
	i2c_clk <= cam_clk;
	rcvd_data <= sr_in(8 downto 1);

   process(clk, req_in)
   begin
      if rising_edge(clk) then
				
         case state is
            when s_idle =>              -- waits for request
               if req_in = '1' then
						sw_start <= '1';      --requests transmission start
                  state <= s_start;
               end if;
				when s_start =>             -- waits for transmission start
					if sw_started = '1' then
						sw_start <= '0';      -- clear the transmission start request
						state <= s_wait;
					end if;
				when s_wait =>              -- wait until transmission completes
					if sw_done = '1' then
						ack_out <= '1';        -- acknowledge the request
						state <= s_done;
					end if;
			   when s_done =>
					if req_in = '0' then     -- wait until the request is cleared
						ack_out <= '0';       -- clear the ack
						state <= s_idle;      -- then wait for next request
					end if;
         end case;
      end if;
   end process;
   
   -- send a two or three phase camera request        
			  
   process(clk, clock_enable)
   begin
      if rising_edge(clk) and (clock_enable = '1') then  -- run on slowed clock rate
         case state2 is
            when s2_idle =>
					data <= 'Z';
					cam_clk <= '1';
               if sw_start = '1' then
						sw_done <= '0';
					   sw_started <= '1';
						phase_counter <= "00";
                  state2 <= s2_start;
               end if;
            when s2_start =>        -- take data high out of tristate
				   sw_started <= '0';
               data <= '1';         -- assumes i2c_clock is living in high state
               state2 <= s2_start2;
            when s2_start2 =>       
               data <= '0';         -- make data low
               state2 <= s2_start3;
            when s2_start3 =>       
               cam_clk <= '0';      -- make i2c clock low
               if mode = "10" then  -- load shift register
                  sr_out <= X"C1";  -- with this for reading
               else                 -- or
                  sr_out <= X"C0";  -- with this for writing
               end if;
               bit_counter <= "1001"; -- nine bits (8 proper and one don't care)
               state2 <= s2_phase;
            when s2_phase =>          -- start phase clock cycle 0110
               cam_clk <= '0';
               if ((mode = "10") and (phase_counter = "01") and  (bit_counter /= "0001")) then
                  data <= 'Z';
               else
                  data <= sr_out(7);
               end if;
               sr_out <= sr_out(6 downto 0) & '1';
               bit_counter <= bit_counter-1;
               state2 <= s2_phase2;
            when s2_phase2 =>
               cam_clk <= '1';
               state2 <= s2_phase3;
            when s2_phase3 =>
               cam_clk <= '1';
               if (mode = "10") and (phase_counter = "01") then
                  sr_in <= sr_in(7 downto 0) & data;
               end if;		
               state2 <= s2_phase4;
            when s2_phase4 =>
               cam_clk <= '0';
					state2 <= s2_phase;
               if bit_counter = "0000" then
                  phase_counter <= phase_counter+1;
                  bit_counter <= "1001";
                  if phase_counter = "00" then  -- just sent device address
                     sr_out <= reg_addr;        -- next send register address (maybe)
                     state2 <= s2_phase;        -- might being doing a read
                  elsif (phase_counter = "01") and (mode = "00") then -- three phase write
                     sr_out <= send_data;
                     state2 <= s2_phase;
                  else
							data <= '0';  
                     state2 <= s2_end;
                  end if;
               end if;
            when s2_end =>
               data <= '0';
               cam_clk <= '1';
               state2 <= s2_end2;
            when s2_end2 =>
               data <= '1';
               state2 <= s2_end3;
            when s2_end3 =>
               data <= 'Z';
					sw_done <= '1';
               state2 <= s2_idle;
         end case;
      end if;
   end process;

	-- camera clock serial clock must be 100 kHz or lower
	-- this unit handles system clocks 75 MHz or lower

	ticker : process(clk)
	begin
		if rising_edge(clk) then
			counter <= counter-1;
			clock_enable <= '0';
			if counter = 0 then
				clock_enable <= '1';
				counter <= conv_std_logic_vector(255,counter'length); -- fourths of cam tic
			end if;
		end if;
	end process;

end Behavioral;

